/*
 * Copyright (C) 2008 Peter Kling
 * 
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 * 
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 'A Kind of Billiard'. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package de.akob.mathematics;

/**
 * Euclidean (<code>double</code>) vectors of arbitrary dimension.
 */
public class MathVector implements Cloneable {
	private final double[] values;
	public final int       dim;
	
	/**
	 * Creates an vector with dimension equal to the length of the given array and initializes it with the array
	 * elements.
	 * 
	 * @param values varargs/array used to generate vector
	 */
	public MathVector(double... values) {
		dim = values.length;
		this.values = values;
	}
	
	/**
	 * Creates the <code>i</code>-dimensional zero vector.
	 * 
	 * @param i dimension of the vector
	 * @throws Illegal argument exception if <code>i</code> is negative
	 */
	public MathVector(int i) {
		if (i <= 0)
			throw new IllegalArgumentException("requested dimension must be positve");
		dim = i;
		values = new double[dim];
	}
	
	/**
	 * Adds the given vector to this vector.
	 * 
	 * @param v vector to be added
	 * @throws IllegalArgumentException if dimension of given vector does not match
	 */
	public void add(MathVector v) {
		if (dim != v.dim)
			throw new IllegalArgumentException("dimensions of given vector does not match");
		for (int i = 1; i <= dim; i++)
			values[i - 1] += v.get(i);
	}
	
	/**
	 * Returns a copy of this vector.
	 * 
	 * @return copy of this vector
	 */
	@Override
	public MathVector clone() {
		MathVector v = new MathVector(values.clone());
		return v;
	}
	
	/**
	 * Returns the requested component of this vector.
	 * 
	 * @param i requested component (first component is 1)
	 * @return requested component
	 */
	public double get(int i) {
		return values[i - 1];
	}
	
	/**
	 * Returns an array containing the vector elements.
	 * 
	 * @return array containing the vector elements
	 */
	public double[] getElements() {
		return values;
	}
	
	/**
	 * Multiplies this vector with a scalar.
	 * 
	 * @param s scalar the vecor is multiplied by
	 */
	public void mul(double s) {
		for (int i = 0; i < dim; i++)
			values[i] *= s;
	}
	
	/**
	 * Returns the norm of this vector.
	 * 
	 * @return norm of this vector
	 */
	public double norm() {
		return Math.sqrt(Mathematics.mul(this, this));
	}
	
	/**
	 * Normalizes this vector.
	 */
	public void normalize() {
		double vectorNorm = norm();
		for (int i = 0; i < dim; i++)
			values[i] /= vectorNorm;
	}
	
	/**
	 * Sets the given vector component to the given value.
	 * 
	 * @param i the component to be set (first component is 1)
	 * @param value the value the component will be set to
	 */
	public void set(int i, double value) {
		values[i - 1] = value;
	}
	
	/**
	 * Returns the squared norm of this vector.
	 * 
	 * @return squared norm of this vector
	 */
	public double sqrNorm() {
		return Mathematics.mul(this, this);
	}
	
	/**
	 * Subtracts the given vector from this one.
	 * 
	 * @param v vector to be subtracted
	 * @throws IllegalArgumentException if dimensions of given vector does not match
	 */
	public void sub(MathVector v) {
		if (dim != v.dim)
			throw new IllegalArgumentException("dimensions of given vector does not match");
		for (int i = 1; i <= dim; i++)
			values[i - 1] -= v.get(i);
	}
	
	/**
	 * Returns a readable representation of this vector.
	 * 
	 * @return readable representation of this vector
	 */
	@Override
	public String toString() {
		String str = "[";
		for (int i = 0; i < values.length - 1; i++)
			str += values[i] + ", ";
		str += values[values.length - 1] + "]";
		return str;
	}
}
